home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / Libraries / GrayImage 2.1 / gray image 2.1 / README < prev    next >
Encoding:
Text File  |  1995-07-30  |  16.1 KB  |  375 lines  |  [TEXT/ttxt]

  1. o run vimage_io.cc, you need to make a subdirectory 'pictures' and
  2. put there any pgm/xwd/tiff file you'd like to be used in the test
  3. (name the file as 512.xwd though, or modify vimage_io.cc). At any
  4. rate, you can get a sample file from
  5.     ftp://replicant.csci.unt.edu/pub/oleg/512.xwd.gz
  6.  
  7. ***** Highlights and idioms
  8.  
  9. Elementary pixel operations: assigning/subtracting/adding etc a value
  10. to all pixels, comparing every pixel with a value,
  11. assigning/adding/subtracting/comparing two images, computing image
  12. extrema, various norms and "scalar" products
  13.  
  14.     IMAGE im1(256,256,8); IMAGE im2(im1);
  15.     im1 = 1; im2 = im1; im1 *= 4;
  16.     assert( im1 == 4 ); assert( im1 > 0 );
  17.     im1.invert(); im1.clear(); im2 <<= 2; im1 = 5; 
  18.     im1 &= 0xfe;  assert( im1==im2 );
  19.     im1.clear(); im1 -= im2; im2 = im1;
  20.     assert( im1 * im1 == norm_2_sqr(im1) );
  21.  
  22.  
  23. Accessing square or rectangular parts of an image without much fuss
  24. (and without moving a lot of stuff around)
  25.  
  26.     im1 = 2*pattern; im2 = pattern;
  27.     rowcol rightbottom(im1.q_nrows()-1,im1.q_ncols()-1);
  28.     rowcol center(im1.q_nrows()/2,im1.q_ncols()/2);
  29.                     // Modifying the pixels only within
  30.                     // the lower left quadrant
  31.     im1.rectangle(center,rightbottom) -= 2*pattern;
  32.     assert( !(im1 == 2*pattern) && !(im1 != 2*pattern) );
  33.  
  34.  
  35. Image i/o: supports reading and writing PGM, XWD and Group G
  36. (grayscale) TIFF file formats with automatical recognition of the
  37. input image file format
  38.                     // Note the "extended" file name
  39.     IMAGE raw_image("zcat ../pictures/lenna.tiff.Z |");
  40.     IMAGE image = raw_image.square_of(256,rowcol(120,100));
  41.     image.write_tiff("/tmp/ior","Original image");
  42.     image.display("Image to display");
  43.     image.write_pgm("| xv -");    // another way to display an image
  44.  
  45. Squeezing/stretching and coercing images. Coercing means that one can
  46. assign one image to another no matter what their dimensions are.  The
  47. source image would be shrunk/stretched to fit. Any dimension ratios
  48. are possible, absolutely any
  49.  
  50.     Test_image = pattern;
  51.     IMAGE blown_out(IMAGE::Expand,Test_image);
  52.     IMAGE blown_shrunk(IMAGE::Shrink,blown_out);
  53.     assert( blown_shrunk == Test_image );
  54.  
  55.     IMAGE shrunk(Test_image.q_nrows()/3+1,
  56.              Test_image.q_ncols()/2,Test_image.q_depth());
  57.     shrunk.coerce(Test_image);
  58.     IMAGE vert_stretched(Test_image.q_nrows()+7,
  59.                  Test_image.q_ncols(),Test_image.q_depth());
  60.     vert_stretched.coerce(Test_image);
  61.         assert( vert_stretched.rectangle(rowcol(0,0),
  62.                          rowcol(Test_image.q_nrows()-1,
  63.                         Test_image.q_ncols()-1))
  64.         == Test_image );
  65.  
  66. Note that the last operation involves an implicit conversion from a
  67. rectangle to an image
  68.  
  69.  
  70. Advanced pixel operations via PixelAction. This is the most natural
  71. (and efficient!) way of doing a "sweeping" image processing (that is,
  72. an operation that is going to involve any pixel in some systematic
  73. way).  Thus, instead of writing a loop over all image rows and
  74. columns, one merely needs to specify what action is to be performed on
  75. a current pixel. The package would take care of the iteration, which
  76. is more efficient than a for() loop. The iteration walks through all
  77. the pixels in a row-by-row fashion; one can use this knowledge if one
  78. wishes to. PixelAction would tell you the location of the pixel being
  79. accessed/modified, while PixelPrimAction won't (if it doesn't
  80. matter). The latter is faster, of course.
  81.  
  82. For example, the following snippet squares all image pixels and
  83. verifies that
  84.  
  85.   {
  86.     IMAGE im(Test_image); IMAGE im1(im);
  87.     im = pattern;
  88.     im.square_of(size,rowcol(0,0)) = -1;
  89.     im1 = im;
  90.     struct SqrImage : public PixelPrimAction {
  91.       void operation(GRAY& pixel) { pixel = sqr((GRAY_SIGNED)pixel); }
  92.     };
  93.     im1.apply(SqrImage());
  94.     assert( sum_over((Rectangle)im1) == im.norm_2_sqr() );
  95.   }
  96.  
  97. Still, lookup tables are better for that purpose (see below). In the
  98. next example, which makes a pin-striped image, the location of the
  99. current pixel _is_ important:
  100.  
  101.     class MakeVStripes : public PixelAction
  102.     {
  103.       GRAY pattern;
  104.       void operation(GRAY& pixel) { pixel = col & 1 ? pattern : 0; }
  105.       public: MakeVStripes(const GRAY _p) : pattern(_p) {}
  106.     };
  107.     Test_image.apply(MakeVStripes(pattern));
  108.  
  109. Finally, the following snippet of the verification code checks out to
  110. see that the pixel actions are indeed executed row-by-row. One
  111. iterator is used to assign to each pixel its own offset from the
  112. beginning of the image, and the other iterator checks it.
  113.  
  114.   {
  115.     cout << "Check to see that PixelAction are executed row-wise" << endl;
  116.     class assign_pixels : public PixelAction {
  117.       const card test_im_nrows, test_im_ncols;
  118.       void operation(GRAY& pixel)
  119.       {
  120.     assert(nrows == test_im_nrows);
  121.     assert(ncols == test_im_ncols);
  122.     pixel = col + row*ncols;
  123.       }
  124.     public: assign_pixels(const IMAGE& im) : 
  125.       test_im_nrows(im.q_nrows()), test_im_ncols(im.q_ncols()) {}
  126.     }
  127.     Test_image.apply(assign_pixels(Test_image));
  128.  
  129.     class check_pixels : public PixelPrimAction {
  130.       GRAY curr_offset;
  131.       void operation(GRAY& pixel) { assert(pixel == curr_offset++); }
  132.       public: check_pixels(void) : curr_offset(0) {}
  133.     }
  134.     Test_image.apply(check_pixels());
  135.   }
  136.  
  137.  
  138.  
  139. Lazy images: instead of returning an object return a "recipe" how to
  140. make it. The full image would be rolled out only when and where it's
  141. needed:
  142.     IMAGE map = FractalMap(order);
  143.  
  144. FractalMap is a *class*, not a simple function. However similar this
  145. looks to a returning of an object, it's dramatically
  146. different. FractalMap() constructs a LazyImage, an object of just a
  147. few bytes long. A special "IMAGE(const LazyImage& recipe)" constructor
  148. follows the recipe and makes the fractal map right in place. No pixel
  149. is moved whatsoever!
  150.  
  151. Since the FractalMap is a class, it can be subclassed to modify the
  152. default behavior (say, to override the default uniform noise generator
  153. with a gaussian noise generator, which tends to produce better looking
  154. clouds)
  155.  
  156.   class GaussNoise : public FractalMap
  157.   {
  158.   public:
  159.     GaussNoise(const card order, const Seeds& seeds,
  160.            const bits_per_pixel=8)
  161.       : FractalMap(order,seeds,bits_per_pixel) {}
  162.     inline int get_noise(const card scale) const {
  163.       long sum = 0;
  164.       for(register int i=0; i<12; i++)
  165.     sum += rand();            // keep the result within 
  166.       return (scale * (sum-(6<<15)))>>17; }    // [-scale/2,scale/2]
  167.   };
  168.  
  169.   IMAGE map = type == 0 ? 
  170.       (LazyImage&)FractalMap(order,FractalMap::Seeds(sul,sll,sur,slr)) :
  171.       (LazyImage&)GaussNoise(order,FractalMap::Seeds(sul,sll,sur,slr));
  172.  
  173. This technique is particularly useful when one needs to construct an
  174. image in some particular way (say, by reading it from a file/database,
  175. or by decoding/decompressing, etc) and return it. Thus, instead of
  176. returning an image, one should always return a LazyImage.
  177.  
  178.     cout << "\tExpansion of the uniform image with a small stain\n";
  179.     Test_image = pattern;
  180.     Test_image(0,0) = 1;
  181.     Test_image(1,1) = 0;
  182.     IMAGE blown_out(IMAGE::Expand,Test_image);
  183.     class BlowImage : public LazyImage
  184.     {
  185.       const IMAGE& orig_image;
  186.       void fill_in(IMAGE& im) const
  187.       {
  188.     for(register int i=0; i<im.q_nrows(); i++)
  189.       for(register int j=0; j<im.q_ncols(); j++)
  190.         im(i,j) = orig_image(i/2,j/2);
  191.       }
  192.     public:
  193.       BlowImage(const IMAGE& image) : 
  194.     LazyImage(2*image.q_nrows(),2*image.q_ncols(),image.q_depth()),
  195.         orig_image(image) {}
  196.     };
  197.     IMAGE another_blown_out = BlowImage(Test_image);
  198.     assert( another_blown_out == blown_out );
  199.  
  200.  
  201.  
  202. Image filtration. The package supports a whole bunch of various image
  203. filtration techniques: convolutional, median, and morphological.  They
  204. are *very* optimized, and *very* fast. All filtration is done
  205. in-place.  My experience tells that it takes noticeably more time to
  206. read an image than to filter it.  It's possible to apply a
  207. convolution/median filter to rows only, or to columns only, or to both
  208. rows and columns.  A convolution kernel can have either int or
  209. rational coefficients; in the latter case, the denominator can be
  210. specified as an exact power of two, or as just any integer. Note, for
  211. a computer, a floating point number is the rational
  212. number. Convolution algorithms are optimized to all these particular
  213. cases (and to the generic case, too).
  214.  
  215.  
  216.     Test_image = vert_line;
  217.     verify_identity(FilterIt(Test_image).
  218.             conv(conv_kernel(1,2,1),FilterIt::Columns)>>=2,vert_line);
  219.     verify_identity(FilterIt(Test_image).
  220.             conv_col(conv_kernel(1,2,3,CommonDenom(6))),vert_line);
  221.     verify_identity(FilterIt(Test_image).
  222.             conv(conv_kernel(0,2,0,over_2_up(1))),vert_line);
  223.     expected = 0;
  224.     expected.rectangle(rowcol(0,vert_line.q_ncols()/2-1),
  225.             rowcol(vert_line.q_nrows()-1,
  226.                    vert_line.q_ncols()/2+1)) = -seed;
  227.     verify_identity(FilterIt(Test_image=vert_line).
  228.             conv(conv_kernel(1,1,1),FilterIt::Rows),expected);
  229.  
  230. The following (intentionally contrived) snippet from the verification
  231. code performs a phase shift of an image through filtration
  232.  
  233.     expected = small_sq(0,0);
  234.     expected.square_of(2,rowcol(sq_row-1,sq_col-1)) =
  235.       small_sq.square_of(2,rowcol(sq_row,sq_col));
  236.     verify_identity(FilterIt(Test_image=small_sq).
  237.             conv(conv_kernel(0,0,2,over_2_up(1))),expected);
  238.  
  239. The package supports median filtration with window sizes of three and
  240. five.  The filtration can be done by rows only, by columns only, or by
  241. rows _and_ by columns. The latter is equivalent to a 2D median
  242. filtration with a diamond-shaped window.
  243.  
  244.     verify_identity(FilterIt(Test_image).median(FilterIt::RowsAndColumns,3),
  245.             expected);
  246.     verify_identity(FilterIt(Test_image).median(FilterIt::RowsAndColumns,5),
  247.             expected);
  248.  
  249.  
  250. Lookup table substitutions are very powerful and very fast. Indeed, in
  251. a typical 512x512 8-bit deep grayscale picture, there are 1/4 M pixels
  252. but only 256 possible pixel values. Therefore, if one needs to perform
  253. some pixel calculation
  254.     new_pixel(i,j) = f(old_pixel(i,j))
  255. the fastest way of accomplishing this task is to create a look-up table,
  256. fill it in like lookup(i) = f(i), i=0..255, and then do
  257.     FilterIt(image).translate(lookup,LookupT::CoerceFringes);
  258. to substitute all "old pixels" of an 'image' with the "new_pixel"
  259. values. The second argument, LookupT::CoerceFringes or
  260. LookupT::LeaveFringes determines what to do with the pixels (if any)
  261. that fall outside of the lookup table range. Note, a lookup table can
  262. have any number of entries, even 1.
  263.  
  264. The following example changes all pixels with value 2 to value 3:
  265.  
  266.     LookupT map(LookupT::MapTo(2,3));
  267.     assert( FilterIt(Test_image=diverse_image).
  268.       translate(map,LookupT::LeaveFringes) != 2 );
  269.  
  270. (the assert statement makes sure that there is no pixel with value 2
  271. left in the translated image).
  272.  
  273. a slight modification
  274.     verify_pixel_value(FilterIt(Test_image=diverse_image).
  275.             translate(map,LookupT::CoerceFringes),3);
  276.  
  277. makes all pixels have the same value, three. Aren't lookup tables
  278. powerful, or what?
  279.  
  280. There are many ways to create lookup tables: allocate a blank table
  281. and fill it in, create an Identity lookup table for a particular image
  282. depth and modify a few entries, or perform a composition of existing
  283. lookup tables.
  284.  
  285. Note
  286.     FilterIt(image).translate(lookup,LookupT::CoerceFringes);
  287. returns a reference to an image after the substitution, so one can
  288. use it in chains like
  289.     assert( FilterIt(Test_image=diverse_image).
  290.       translate(map,LookupT::LeaveFringes) != seed );
  291.  
  292.  
  293. ***** Grand plans
  294.  
  295. - a PixelAction-like class for lookup table elements
  296. - in PixelAction, a method 'operation(GRAY& pixel)' should return bool
  297.   (if it returns false, the traversal is terminated). Maybe there should
  298.   be another class, like PixelControlledAction (which also would have
  299.   specifications as to where to start and where/when to end the traversal),
  300.   OR traversal classes for Rectangles
  301. - make operation() method of PixelAction non-virtual, and make IMAGE::apply()
  302.   templated to a PixelAction-like class (when gcc starts supporting member
  303.   function templates)
  304. - make IMAGE::allocate() virtual, or define a class ImageData that contains
  305.   only the pixel matrix without indices. So when one needs to handle an
  306.   array of images (frames or color-separation planes), the memory won't be
  307.   wasted on many (mostly redundant) indices
  308. - add spans (by generalizing rowcol's), 1D, 2D spans, maybe even regions (non-
  309.   rectangular spans). Add IMAGE constructors from spans. Use spans
  310.   as generalized indices to take slices of an image
  311. - add a LazyMask to achieve
  312.     B(A==5) = 4;
  313.   (that is, for all i,j; if(A(i,j) ==5) B(i,j) = 4; ), like in Matlab.
  314. - simple color imagery with transformations among various tristimulus
  315.   representations
  316. - support addition of a rectangle to an image or an image to a rectangle,
  317.   or comparison between a rectangle and an image
  318. - add convolutions with a 5-point kernel and with 3x3 (non-separable) kernels
  319. - add a "non-deterministic averaging" filtering (like in LIFE, only
  320.   survival of a pixel is probabilistic, and the chance of survival depends
  321.   on how much a pixel sticks out of its neighborhood)
  322.  
  323.  
  324. ***** Revision history
  325.  
  326. version 2.0
  327. Makefile is much more user-friendly.
  328. Introduced card (typedef'ed as unsigned int) for row and col dimensions,
  329. indices and other quantities which are always non-negative.
  330. Added PixelAction and PixelPrimAction, classes to do a specific (maybe
  331. very complex) operation on every pixel regardless of its position
  332. (PixelPrimAction) or with regard to its position (PixelAction). The image
  333. is traversed row-by-row.
  334. Introduced LazyImage (which is what to return instead of IMAGE).
  335. bool is used when appropriate.
  336. Removed GNU extensions, code is made very portable.
  337. Sundry of optimizations.
  338. IMAGE::operator(): now it returns GRAY (the pixel itself) in const contexts,
  339. otherwise, it returns a reference to a pixel.
  340. read_xwd/read_pgm use "new style" with the PixelAction stuff.
  341. Median filtration re-worked; can be done now only by rows, only by cols,
  342. and both by Rows and columns. It's heavily optimized and must be very fast.
  343. Added convolutions: by rows, by cols, and separable 2D. Very efficient:
  344. kernel coeffs can be int, rational with power2 common denom, and
  345. rational. The three cases are handled separately (yet uniformly) and very
  346. efficiently (almost as much as one can get). Filtering is done "in-place"
  347. Added assignments with stretching/squeezing an image to fit (IMAGE::coerce()),
  348. the stretching/squeezing is done by arbitrary ratio! (the sizes of the
  349. original and the assigned image can be anything, not necessarily int).
  350. Added lookup table translations.
  351.  
  352. version 1.15, Feb 8, 1995
  353. Cosmetic changes to please gcc 2.6.3 and adjust to the new version
  354. of endian_io.h
  355.  
  356. version 1.14, Mar 24, 1994 (previously posted on comp.sources.misc)
  357. Added support for reading/writing PGM and TIFF image file formats
  358. (in addition to the existed support for XWD format). Default write
  359. is in XWD format.
  360. Added image histogram equalization.
  361. Generalizing the Square_area class to the Rectangle class, which handles
  362. arbitrary rectangular areas of the image.
  363. Added comparison predicates that check a relation between an int and all
  364. pixels of the image.
  365. Added Abs() for putting down negative pixels, and application of a generic
  366. user-supplied function to every pixel of the image.
  367. Added +, -, *, >>, and << operations on rowcol (returning rowcol)
  368. Added assignments, comparison, and arithmetics (offsetting) on objects of
  369. the class rowcol.
  370. Added finding Extrema pixel values and image normalization for display.
  371.  
  372. version 1.1, Apr 3, 1992
  373. Initial revision
  374.  
  375.